%{
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include "ast.h"
#include "internal.h"
#include "tactics.h"
#include "yy.h"

#ifdef DEBUG
#define SHOW_PROD(i) fprintf(stderr, "yacc: Product\t- #%s\n", (i))
#else
#define SHOW_PROD(i)
#endif
%}

%token INT
%token ENUM
%token BOOL
%token EVENT
%token CONT

%token TOP
%token BOTTOM
%token RANDOM
%token VALUE
%token FILL
%token SHUFFLE
%token PEEK
%token REMOVE
%token INSERT
%token EXTRACT
%token COMBINE
%token SIZE

%token EQ
%token NE
%token LT
%token LE
%token GT
%token GE

%token ADD
%token SUB
%token MUL
%token DIV
%token MOD
%token INC
%token DEC

%token AND
%token OR
%token NOT
%token TRUE
%token FALSE

%token IF
%token ELSE
%token WHILE
%token DO
%token FOR
%token BREAK
%token CONTINUE
%token RETURN
%token CALL
%token TRANS
%token INPUT
%token OUTPUT

%token LPAREN
%token RPAREN
%token LBRACE
%token RBRACE
%token LBRACKET
%token RBRACKET

%token ASSIGN
%token SEMICOLON
%token COMMA
%token QUESTION
%token COLON

%union {
	int integer;
	char *identifier;
	char *string;

	struct translationunit *tranunit;

	struct i_list *id_list;

	struct e_enumerate *enumerate;
	struct e_elemlist *enum_elemlist;
	struct e_element *enum_element;

	struct v_variable *variable;
	struct v_list *var_list;

	struct f_function *function;

	struct s_statement *statement;
	struct s_list *stmt_list;

	struct t_type *type;

	struct x_expression *expression;
	struct x_list *exp_list;

	struct p_position *position;
}

%token <integer> INTEGER
%token <identifier> IDENTIFIER
%token <string> STRING

%type <tranunit> TranslationUnit

%type <enumerate> EnumerateDefinition
%type <enum_elemlist> EnumerateElementList
%type <enum_element> EnumerateElement

%type <id_list> IdentifierList

%type <var_list> VariableDeclaration
%type <var_list> VariableDeclarationList
%type <var_list> ParameterList
%type <variable> ParameterDeclaration

%type <function> EventDefinition
%type <function> FunctionDefinition

%type <statement> LocalStatement
%type <stmt_list> LocalStatementList

%type <type> TypeExpression
%type <type> TypeExpressionBase
%type <type> TypeExpressionArray
%type <type> TypeExpressionEnumerate
%type <type> TypeExpressionContainer

%type <expression> Expression
%type <expression> ExpressionAssignment
%type <expression> ExpressionConditional
%type <expression> ExpressionOR
%type <expression> ExpressionAND
%type <expression> ExpressionEQ
%type <expression> ExpressionREL
%type <expression> ExpressionADD
%type <expression> ExpressionMUL
%type <expression> ExpressionPrefix
%type <expression> ExpressionPostfix
%type <expression> ExpressionPrimary
%type <expression> ExpressionArray
%type <expression> ExpressionDataStructure
%type <exp_list> ExpressionList

%type <position> PositionExpression

%start Tactics

%%

Tactics
	: TranslationUnit { SHOW_PROD("Tactics"); unit = $1; }
	;

TranslationUnit
	: { SHOW_PROD("TranslationUnit"); $$ = con_tu_init(); }
	| TranslationUnit EnumerateDefinition { SHOW_PROD("TranslationUnit"); $$ = con_tu_add_enum($1, $2); }
	| TranslationUnit VariableDeclaration { SHOW_PROD("TranslationUnit"); $$ = con_tu_add_var($1, $2); }
	| TranslationUnit EventDefinition { SHOW_PROD("TranslationUnit"); $$ = con_tu_add_event($1, $2); }
	| TranslationUnit FunctionDefinition { SHOW_PROD("TranslationUnit"); $$ = con_tu_add_function($1, $2); }
	;

EnumerateDefinition
	: ENUM IDENTIFIER LBRACE EnumerateElementList RBRACE SEMICOLON { SHOW_PROD("EnumerateDefinition"); $$ = con_e_base($2, $4); }
	| ENUM IDENTIFIER LT IdentifierList GT SEMICOLON { SHOW_PROD("EnumerateDefinition"); $$ = con_e_comb($2, $4); }
	;

EnumerateElementList
	: EnumerateElement { SHOW_PROD("EnumerateElementList"); $$ = con_e_elemlist_init($1); }
	| EnumerateElementList COMMA EnumerateElement { SHOW_PROD("EnumerateElementList"); $$ = con_e_elemlist_add($1, $3); }
	;

EnumerateElement
	: IDENTIFIER { SHOW_PROD("EnumerateElement"); $$ = con_e_elem($1, -1, NULL); }
	| IDENTIFIER ASSIGN INTEGER { SHOW_PROD("EnumerateElement"); $$ = con_e_elem($1, $3, NULL); }
	| IDENTIFIER STRING { SHOW_PROD("EnumerateElement"); $$ = con_e_elem($1, -1, $2); }
	| IDENTIFIER ASSIGN INTEGER STRING { SHOW_PROD("EnumerateElement"); $$ = con_e_elem($1, $3, $4); }
	;

IdentifierList
	: IDENTIFIER { SHOW_PROD("IdentifierList"); $$ = con_i_list_init($1); }
	| IdentifierList COMMA IDENTIFIER { SHOW_PROD("IdentifierList"); $$ = con_i_list_add($1, $3); }
	;

VariableDeclarationList
	: { SHOW_PROD("VariableDeclarationList"); $$ = con_v_list_init(); }
	| VariableDeclarationList VariableDeclaration { SHOW_PROD("VariableDeclarationList"); $$ = con_v_list_combine($1, $2); }
	;

VariableDeclaration
	: TypeExpression IdentifierList SEMICOLON { SHOW_PROD("VariableDeclaration"); $$ = con_v_list_create($1, $2); }
	;

EventDefinition
	: EVENT IDENTIFIER LBRACE VariableDeclarationList LocalStatementList RBRACE { SHOW_PROD("EventDefinition"); $$ = con_f_event($2, $4, $5); }
	;

FunctionDefinition
	: TypeExpression IDENTIFIER LPAREN ParameterList RPAREN LBRACE VariableDeclarationList LocalStatementList RBRACE { SHOW_PROD("FunctionDefinition"); $$ = con_f_function($1, $2, $4, $7, $8); }
	;

ParameterList
	: ParameterDeclaration { SHOW_PROD("ParameterList"); $$ = con_v_list_init(); $$ = con_v_list_add($$, $1); }
	| ParameterList COMMA ParameterDeclaration { SHOW_PROD("ParameterList"); $$ = con_v_list_add($1, $3); }
	;

ParameterDeclaration
	: TypeExpression IDENTIFIER { SHOW_PROD("ParameterDeclaration"); $$ = con_v_variable($1, $2); }
	;

LocalStatementList
	: { SHOW_PROD("LocalStatementList"); $$ = con_s_list_init(); }
	| LocalStatementList LocalStatement { SHOW_PROD("LocalStatementList"); $$ = con_s_list_add($1, $2); }
	;

LocalStatement
	: Expression SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_expression($1); }
	| IF LPAREN Expression RPAREN LBRACE LocalStatementList RBRACE { SHOW_PROD("LocalStatement"); $$ = con_s_if($3, $6); }
	| IF LPAREN Expression RPAREN LBRACE LocalStatementList RBRACE ELSE LBRACE LocalStatementList RBRACE { SHOW_PROD("LocalStatement"); $$ = con_s_ifelse($3, $6, $10); }
	| WHILE LPAREN Expression RPAREN LBRACE LocalStatementList RBRACE { SHOW_PROD("LocalStatement"); $$ = con_s_while($3, $6); }
	| DO LBRACE LocalStatementList RBRACE WHILE LPAREN Expression RPAREN SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_dowhile($3, $7); }
	| FOR LPAREN Expression SEMICOLON Expression SEMICOLON Expression RPAREN LBRACE LocalStatementList RBRACE { SHOW_PROD("LocalStatement"); $$ = con_s_for($3, $5, $7, $10); }
	| BREAK SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_break(); }
	| CONTINUE SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_continue(); }
	| RETURN SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_return(NULL); }
	| RETURN LPAREN Expression RPAREN SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_return($3); }
	| CALL IDENTIFIER SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_call($2); }
	| TRANS IDENTIFIER SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_trans($2); }
	| INPUT LPAREN Expression COMMA Expression RPAREN SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_input($3, $5); }
	| OUTPUT LPAREN Expression COMMA STRING RPAREN SEMICOLON { SHOW_PROD("LocalStatement"); $$ = con_s_output($3, $5); }
	;

TypeExpression
	: TypeExpressionBase { SHOW_PROD("TypeExpression"); $$ = $1; }
	| TypeExpressionArray { SHOW_PROD("TypeExpression"); $$ = $1; }
	;

TypeExpressionBase
	: INT { SHOW_PROD("TypeExpressionBase"); $$ = con_t_int(); }
	| BOOL { SHOW_PROD("TypeExpressionBase"); $$ = con_t_bool(); }
	| TypeExpressionEnumerate { SHOW_PROD("TypeExpressionBase"); $$ = $1; }
	| TypeExpressionContainer { SHOW_PROD("TypeExpressionBase"); $$ = $1; }
	;

TypeExpressionEnumerate
	: ENUM IDENTIFIER { SHOW_PROD("TypeExpressionEnumerate"); $$ = con_t_enum($2); }
	;

TypeExpressionContainer
	: CONT LT TypeExpressionBase GT { SHOW_PROD("TypeExpressionContainer"); $$ = con_t_cont($3); }
	;

TypeExpressionArray
	: TypeExpressionBase LBRACKET Expression RBRACKET { SHOW_PROD("TypeExpressionArray"); $$ = con_t_array_int($1, $3); }
	| TypeExpressionBase LBRACKET ENUM IDENTIFIER RBRACKET { SHOW_PROD("TypeExpressionArray"); $$ = con_t_array_enum($1, $4); }
	;

	/* EXPRESSIONS BELOW */

ExpressionList
	: Expression { SHOW_PROD("ExpressionList"); $$ = con_x_list_init(); $$ = con_x_list_add($$, $1); }
	| ExpressionList COMMA Expression { SHOW_PROD("ExpressionList"); $$ = con_x_list_add($1, $3); }
	;

Expression
	: ExpressionAssignment { SHOW_PROD("Expression"); $$ = $1; }
	;

ExpressionAssignment
	: ExpressionConditional { SHOW_PROD("ExpressionAssignment"); $$ = $1; }
	| ExpressionConditional ASSIGN ExpressionAssignment { SHOW_PROD("ExpressionAssignment"); $$ = con_x_bin(X_ASSIGN, $1, $3); }
	;

ExpressionConditional
	: ExpressionOR { SHOW_PROD("ExpressionConditional"); $$ = $1; }
	| ExpressionOR QUESTION Expression COLON ExpressionConditional { SHOW_PROD("ExpressionConditional"); $$ = con_x_tri(X_COND, $1, $3, $5); }
	;

ExpressionOR
	: ExpressionAND { SHOW_PROD("ExpressionOR"); $$ = $1; }
	| ExpressionOR OR ExpressionAND { SHOW_PROD("ExpressionOR"); $$ = con_x_bin(X_OR, $1, $3); }
	;

ExpressionAND
	: ExpressionEQ { SHOW_PROD("ExpressionAND"); $$ = $1; }
	| ExpressionAND AND ExpressionEQ { SHOW_PROD("ExpressionAND"); $$ = con_x_bin(X_AND, $1, $3); }
	;

ExpressionEQ
	: ExpressionREL { SHOW_PROD("ExpressionEQ"); $$ = $1; }
	| ExpressionEQ EQ ExpressionREL { SHOW_PROD("ExpressionEQ"); $$ = con_x_bin(X_EQ, $1, $3); }
	| ExpressionEQ NE ExpressionREL { SHOW_PROD("ExpressionEQ"); $$ = con_x_bin(X_NE, $1, $3); }
	;

ExpressionREL
	: ExpressionADD { SHOW_PROD("ExpressionREL"); $$ = $1; }
	| ExpressionREL LT ExpressionADD { SHOW_PROD("ExpressionREL"); $$ = con_x_bin(X_LT, $1, $3); }
	| ExpressionREL LE ExpressionADD { SHOW_PROD("ExpressionREL"); $$ = con_x_bin(X_LE, $1, $3); }
	| ExpressionREL GT ExpressionADD { SHOW_PROD("ExpressionREL"); $$ = con_x_bin(X_GT, $1, $3); }
	| ExpressionREL GE ExpressionADD { SHOW_PROD("ExpressionREL"); $$ = con_x_bin(X_GE, $1, $3); }
	;

ExpressionADD
	: ExpressionMUL { SHOW_PROD("ExpressionADD"); $$ = $1; }
	| ExpressionADD ADD ExpressionMUL { SHOW_PROD("ExpressionADD"); $$ = con_x_bin(X_ADD, $1, $3); }
	| ExpressionADD SUB ExpressionMUL { SHOW_PROD("ExpressionADD"); $$ = con_x_bin(X_SUB, $1, $3); }
	;

ExpressionMUL
	: ExpressionPrefix { SHOW_PROD("ExpressionMUL"); $$ = $1; }
	| ExpressionMUL MUL ExpressionPrefix { SHOW_PROD("ExpressionMUL"); $$ = con_x_bin(X_MUL, $1, $3); }
	| ExpressionMUL DIV ExpressionPrefix { SHOW_PROD("ExpressionMUL"); $$ = con_x_bin(X_DIV, $1, $3); }
	| ExpressionMUL MOD ExpressionPrefix { SHOW_PROD("ExpressionMUL"); $$ = con_x_bin(X_MOD, $1, $3); }
	;

ExpressionPrefix
	: ExpressionPostfix { SHOW_PROD("ExpressionPrefix"); $$ = $1; }
	| INC ExpressionPostfix { SHOW_PROD("ExpressionPrefix"); $$ = con_x_un(X_LINC, $2); }
	| DEC ExpressionPostfix { SHOW_PROD("ExpressionPrefix"); $$ = con_x_un(X_LDEC, $2); }
	| ADD ExpressionPostfix { SHOW_PROD("ExpressionPrefix"); $$ = $2; }
	| SUB ExpressionPostfix { SHOW_PROD("ExpressionPrefix"); $$ = con_x_un(X_LSUB, $2); }
	| NOT ExpressionPostfix { SHOW_PROD("ExpressionPrefix"); $$ = con_x_un(X_NOT, $2); }
	;

ExpressionPostfix
	: ExpressionPrimary { SHOW_PROD("ExpressionPostfix"); $$ = $1; }
	| ExpressionPostfix INC { SHOW_PROD("ExpressionPostfix"); $$ = con_x_un(X_RINC, $1); }
	| ExpressionPostfix DEC { SHOW_PROD("ExpressionPostfix"); $$ = con_x_un(X_RDEC, $1); }
	;

ExpressionPrimary
	: LPAREN Expression RPAREN { $$ = $2; }
	| INTEGER { $$ = con_x_int($1); }
	| TRUE { $$ = con_x_bool(true); }
	| FALSE { $$ = con_x_bool(false); }
	| IDENTIFIER { $$ = con_x_id($1); }
	| IDENTIFIER LPAREN ExpressionList RPAREN { $$ = con_x_call($1, $3); }
	| ExpressionArray { $$ = $1; }
	| ExpressionDataStructure {$$ = $1; }
	;

ExpressionArray
	: IDENTIFIER LBRACKET Expression RBRACKET { $$ = con_x_array($1, $3); }
	;

ExpressionDataStructure
	: RANDOM LPAREN TypeExpressionEnumerate RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_random($3); }
	| VALUE LPAREN Expression RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_value($3); }
	| SIZE LPAREN Expression RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_size($3); }
	| FILL LPAREN Expression RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_fill($3); }
	| SHUFFLE LPAREN Expression RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_shuffle($3); }
	| PEEK LPAREN Expression COMMA PositionExpression RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_peek($3, $5); }
	| REMOVE LPAREN Expression COMMA PositionExpression RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_remove($3, $5); }
	| INSERT LPAREN Expression COMMA PositionExpression COMMA Expression RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_insert($3, $5, $7); }
	| EXTRACT LPAREN Expression COMMA Expression RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_extract($3, $5); }
	| COMBINE LPAREN ExpressionList RPAREN { SHOW_PROD("ExpressionDataStructure"); $$ = con_x_ds_combine($3); }
	;

PositionExpression
	: TOP { SHOW_PROD("PositionExpression"); $$ = con_p_top(); }
	| BOTTOM { SHOW_PROD("PositionExpression"); $$ = con_p_bottom(); }
	| RANDOM { SHOW_PROD("PositionExpression"); $$ = con_p_random(); }
	| Expression { SHOW_PROD("PositionExpression"); $$ = con_p_expression($1); }
	;

%%

